package corp;

import hackathon1212.ServiceResponse;
import hackathon1212.ServletPojoAdapter;
import hackathon1212.Status;

import java.util.Map;

public class CorporateService extends ServletPojoAdapter {

	private static final long serialVersionUID = -2308362312972565022L;

	/**
	 * Corporate Cards feeble attempt at a service.
	 * 
	 * Called using "corp/corpservice.json?function=..."
	 * 
	 * See documentation for supported functions and the paramaters to pass.
	 * 
	 * @param params
	 *            Arguments user must supply in order to call corporate
	 *            services. All calls require a "&function=" which defines which
	 *            service to use. Depending on the function a second argument in
	 *            brackets is also needed. 
	 *            "GetCorpIDs" 
	 *            "GetCorpByID" (&corpID=)
	 *            "GetCorpExposure" (&corpID=) 
	 *            "GetCorpNameByID" (&corpID=)
	 *            "GetControlAccount" (&controlAccount=) 
	 *            "GetCardAccount" (&cardNumber=) 
	 *            "GetCMHierarchy" (&cardNumber=) 
	 *            "GetCardAccounts"
	 *            "GetCardAccountBalance" (&cardNumber=)
	 * 
	 * @return a ServiceResponse object containing a Status (OK, FAIL, WARNING)
	 *         and the returned object itself (or null if FAIL, WARNING)
	 */
	protected ServiceResponse getBusinessMethod(Map<String, String[]> params) {
		final String[] args = params.get("function");
		if (args != null && args.length > 0) {
			// All Services returned here
			if (args[0].equals("GetCorpIDs")) {
				return GetCorpIDs();
			}
			if (args[0].equals("GetCorpByID")) {
				return GetCorpByID(params);
			}
			if (args[0].equals("GetCorpNameByID")) {
				return GetCorpNameByID(params);
			}
			if (args[0].equals("GetCorpExposure")) {
				return GetCorpExposure(params);
			}
			if (args[0].equals("GetControlAccount")) {
				return GetControlAccount(params);
			}
			if (args[0].equals("GetCardAccount")) {
				return GetCardAccount(params);
			}
			if (args[0].equals("GetCMHierarchy")) {
				return getCMHierarchy(params);
			}
			if (args[0].equals("GetCardAccounts")) {
				return GetCardAccounts(params);
			}
			if (args[0].equals("GetCardAccountBalance")) {
				return GetCardAccountBalance(params);
			}

			// Return if no correct argument supplied
			return new ServiceResponse(Status.WARNING, null);
		}
		return new ServiceResponse(Status.FAIL, null);
	}
	
	/**
	 * Corporate Cards feeble attempt at a post service.
	 * 
	 * Called using "corp/corpservice.json?function=..." using post
	 * 
	 * See documentation for supported functions and the paramaters to pass.
	 * 
	 * @param params
	 *            Arguments user must supply in order to call corporate
	 *            services. All calls require a "&function=" which defines which
	 *            service to use. Depending on the function a second argument in
	 *            brackets is also needed. 
	 *            <b>"transferCM" (&cardNumber=,&toCorp=,&toCtrl=)</b>
	 *            Moves the card account from its current Corporation & control
	 *            account to the supplied values. Corp & Ctrl balances are updated.
	 *             
	 *            <b>"updateCM" (&cardNumber=,&key=,&value)</b>
	 *            Updates the supplied card number with the given field / value
	 *            pair. Can contain unlimited values, however no duplicates.
	 *            
	 *            <b>"updateCorp" (&corpID=,&key=,&value)</b>
	 *            Updates the supplied corporation number with the given field / value
	 *            pair. Can contain unlimited values, however no duplicates.
	 * 
	 * 
	 * 			  <b>"updateCtrl" (&corpID=,&ctrlID=,&key=,&value)</b>
	 *            Updates the supplied corporation number with the given field / value
	 *            pair. Can contain unlimited values, however no duplicates.
	 *            
	 *            <b>"addCtrl" (&corpID=,&newCtrlID=)</b>
	 *            Updates the supplied corporation number with the given field / value
	 *            pair. Can contain unlimited values, however no duplicates.
	 * 
	 * @return a ServiceResponse object containing a Status (OK, FAIL, WARNING)
	 *         and the returned object itself (or null if FAIL, WARNING)
	 */
	protected ServiceResponse postBusinessMethod(Map<String, String[]> params) {

		final String[] args = params.get("function");
		if (args != null && args.length > 0) {
			// Move CM from
			if (args[0].equals("transferCM")) {
				return TransferCM(params);
			}

			if (args[0].equals("updateCM")) {
				return UpdateCM(params);
			}

			if (args[0].equals("updateCorp")) {
				return UpdateCorp(params);
			}
			
			if (args[0].equals("updateCtrl")) {
				return UpdateCtrl(params);
			}
			
			if (args[0].equals("addCtrl")) {
				return AddCtrl(params);
			}
			
			// Return if no correct argument supplied
			return new ServiceResponse(Status.WARNING, null);
		}
		return new ServiceResponse(Status.FAIL, null);
	}

	private ServiceResponse UpdateCM(Map<String, String[]> params) {
		final String[] key = params.get("key");
		final String[] value = params.get("value");
		final String[] cardNumber = params.get("cardNumber");
		if ((key == null || key.length < 0)
				|| (value == null || value.length < 0)
				|| (cardNumber == null || cardNumber.length < 0)) {
			return new ServiceResponse(Status.WARNING, null);
		}
		CardAccount ca = getCardAccount(cardNumber[0]);
		if (ca == null)
			return new ServiceResponse(Status.WARNING, "Card not found");
		ca.setAdditionalData(key[0], value[0]);
		return new ServiceResponse(Status.OK, ca);
	}

	private ServiceResponse UpdateCorp(Map<String, String[]> params) {
		final String[] key = params.get("key");
		final String[] value = params.get("value");
		final String[] corpID = params.get("corpID");
		if ((key == null || key.length < 0)
				|| (value == null || value.length < 0)
				|| (corpID == null || corpID.length < 0)) {
			return new ServiceResponse(Status.WARNING, null);
		}
		Corporation c = CorporateDAO.getInstance().fetchCorporation(corpID[0]);
		if (c == null) 
			return new ServiceResponse(Status.WARNING, "Corporation not found");
		c.setAdditionalData(key[0], value[0]);
		return new ServiceResponse(Status.OK, c);
	}
	
	private ServiceResponse UpdateCtrl(Map<String, String[]> params) {
		final String[] key = params.get("key");
		final String[] value = params.get("value");
		final String[] corpID = params.get("corpID");
		final String[] ctrlID = params.get("ctrlID");
		if ((key == null || key.length < 0)
				|| (value == null || value.length < 0)
				|| (corpID == null || corpID.length < 0)
				|| (ctrlID == null || ctrlID.length < 0)) {
			return new ServiceResponse(Status.WARNING, null);
		}
		Corporation c = CorporateDAO.getInstance().fetchCorporation(corpID[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, "Corporation not found");
		ControlAccount ca = c.getControlAccount(ctrlID[0]);
		if (ca == null)
			return new ServiceResponse(Status.WARNING, "Control Account not found");
		ca.setAdditionalData(key[0], value[0]);
		return new ServiceResponse(Status.OK, ca);
	}
	
	private ServiceResponse getCMHierarchy(Map<String, String[]> params) {
		final String[] cm = params.get("cardNumber");
		if (cm == null || cm.length < 0) {
			return new ServiceResponse(Status.WARNING, null);
		}
		CardIndex cmIx = CorporateDAO.getInstance().getCardIndex(cm[0]);
		if (cmIx == null) {
			return new ServiceResponse(Status.WARNING, null);
		}
		return new ServiceResponse(Status.OK, cmIx);
	}
	
	private ServiceResponse AddCtrl(Map<String, String[]> params) {
		final String[] corpID = params.get("corpID");
		final String[] newCtrlID = params.get("newCtrlID");
		final String[] newCtrlName = params.get("newCtrlName");
		if ((corpID == null || corpID.length < 0)
				|| (newCtrlID == null || newCtrlID.length < 0)
				|| (newCtrlName == null || newCtrlName.length < 0)) {
			return new ServiceResponse(Status.WARNING, null);
		}
		Corporation c = CorporateDAO.getInstance().fetchCorporation(corpID[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, "Corporation not found");
		ControlAccount ca = new ControlAccount(newCtrlID[0],newCtrlName[0]);
		if (c.addControlAccount(ca))
			return new ServiceResponse(Status.OK, ca);
		return new ServiceResponse(Status.WARNING, "Control Account already exists");
	}

	// transfer cm
	private ServiceResponse TransferCM(Map<String, String[]> params) {
		StringBuffer buff = new StringBuffer();
		final String[] toCorpID = params.get("toCorp");
		final String[] toCtrlID = params.get("toCtrl");
		final String[] cardNumber = params.get("cardNumber");
		if ((toCorpID == null || toCorpID.length < 0)
				|| (toCtrlID == null || toCtrlID.length < 0)
				|| (cardNumber == null || cardNumber.length < 0)) {
			buff.append("Invalid input params");
			return new ServiceResponse(Status.WARNING, buff.toString());
		}
		String toCorp = toCorpID[0];
		String toCtrl = toCtrlID[0];
		Corporation toC = CorporateDAO.getInstance().fetchCorporation(toCorp);
		if (toC == null) {
			buff.append("New Corp does not exist");
			return new ServiceResponse(Status.WARNING, buff.toString());
		}
		// New control account
		ControlAccount toA = toC.getControlAccount(toCtrl);
		if (toA == null) {
			buff.append("New Ctrl does not exist");
			return new ServiceResponse(Status.WARNING, buff.toString());
		}
		String cm = cardNumber[0];
		// Card account lookup (to get old ctrl / corp details)
		CardIndex cmIx = CorporateDAO.getInstance().getCardIndex(cm);
		if (cmIx == null) {
			buff.append("Card account not found");
			return new ServiceResponse(Status.WARNING, buff.toString());
		}
		Corporation oldC = CorporateDAO.getInstance().fetchCorporation(
				cmIx.getCorp());
		// Check not same corporation as what card is already under
	//	if (oldC.getCorporationNumber() == toC.getCorporationNumber()) {
	//		buff.append("Different Corp number required");
	//		return new ServiceResponse(Status.WARNING, buff.toString());
	//	}

		ControlAccount oldCa = oldC.getControlAccount(cmIx.getCtrl());

		// Check not same control account as current CM control account
		if ((oldC.getCorporationNumber() == toC.getCorporationNumber()) &&
			(oldCa.getControlAccountNumber() == toA.getControlAccountNumber())) {
			buff.append("Existing Company / Control Account Entered");
			return new ServiceResponse(Status.WARNING, buff.toString());
		}

		CardAccount ca = oldCa.getCardAccount(cm);
		boolean b = moveCM(buff, oldC, oldCa, toC, toA, ca);
		if (b)
			return new ServiceResponse(Status.OK, buff.toString());
		else
			return new ServiceResponse(Status.WARNING, buff.toString());
	}

	/**
	 * Get a CardAccount object based on supplied card number. Uses index for
	 * quicker access.
	 * @param cm
	 * @return
	 */
	protected CardAccount getCardAccount(String cm) {
		// Card account lookup (& old ctrl / corp details)
		CardIndex cmIx = CorporateDAO.getInstance().getCardIndex(cm);
		if (cmIx == null) {
			return null;
		}
		Corporation oldC = CorporateDAO.getInstance().fetchCorporation(
				cmIx.getCorp());
		ControlAccount oldCa = oldC.getControlAccount(cmIx.getCtrl());
		CardAccount ca = oldCa.getCardAccount(cm);
		return ca;
	}
	
	

	private boolean moveCM(StringBuffer buff, Corporation oldCorp,
			ControlAccount oldCtrl, Corporation newCorp,
			ControlAccount newCtrl, CardAccount acc) {
		String result;
		if (newCtrl.addCardAccount(acc)) {
			newCtrl.debitControlAccount(acc.getBalance());
			newCorp.debitCorporation(acc.getBalance());
			oldCtrl.removeCardAccount(acc);
			oldCtrl.creditControlAccount(acc.getBalance());
			oldCorp.creditCorporation(acc.getBalance());
			result = "CM " + acc.getCardNumber() + " transferred from "
					+ oldCorp.getCorporationName() + "("
					+ oldCorp.getCorporationNumber() + ","
					+ oldCtrl.getControlAccountNumber() + ")" + " to "
					+ newCorp.getCorporationName() + "("
					+ newCorp.getCorporationNumber() + ","
					+ newCtrl.getControlAccountNumber() + ")";
			// Update old CardIndex entry
			CorporateDAO.getInstance().putCardIndex(
					acc.getCardNumber(),
					new CardIndex(newCorp.getCorporationNumber(), newCtrl
							.getControlAccountNumber()));
			buff.append(result);
			return true;
		}
		buff.append("Could not add card");
		return false;
	}

	protected ServiceResponse GetCorpIDs() {
		Map<String, Corporation> c = CorporateDAO.getInstance().getCorporation();
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c.keySet());
	}

	private ServiceResponse GetCorpNameByID(Map<String, String[]> params) {
		final String[] args = params.get("corpID");
		if (args == null || args.length < 0) {
			return new ServiceResponse(Status.FAIL, null);
		}
		Corporation c = CorporateDAO.getInstance().fetchCorporation(args[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c.getCorporationName());
	}

	private ServiceResponse GetCorpExposure(Map<String, String[]> params) {
		final String[] args = params.get("corpID");
		if (args == null || args.length < 0) {
			return new ServiceResponse(Status.FAIL, null);
		}
		Corporation c = CorporateDAO.getInstance().fetchCorporation(args[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c.getFormattedBalance());

	}

	private ServiceResponse GetCorpByID(Map<String, String[]> params) {
		final String[] args = params.get("corpID");
		if (args == null || args.length < 0) {
			return new ServiceResponse(Status.FAIL, null);
		}
		Corporation c = CorporateDAO.getInstance().fetchCorporation(args[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c);

	}

	private ServiceResponse GetControlAccount(Map<String, String[]> params) {
		final String[] args = params.get("controlAccount");
		if (args == null || args.length < 0) {
			return new ServiceResponse(Status.FAIL, null);
		}
		ControlAccount c = CorporateDAO.getInstance().fetchControlAccount(
				args[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c);
	}

	private ServiceResponse GetCardAccount(Map<String, String[]> params) {
		final String[] args = params.get("cardNumber");
		if (args == null || args.length < 0) {
			return new ServiceResponse(Status.FAIL, null);
		}
		CardAccount c = CorporateDAO.getInstance().fetchCardAccount(args[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c);
	}
	
	private ServiceResponse GetCardAccounts(Map<String, String[]> params) {
		Map<String, CardIndex> c = CorporateDAO.getInstance().getCardIndexObj();
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c.keySet());
	}

	private ServiceResponse GetCardAccountBalance(Map<String, String[]> params) {
		final String[] args = params.get("cardNumber");
		if (args == null || args.length < 0) {
			return new ServiceResponse(Status.FAIL, null);
		}
		CardAccount c = CorporateDAO.getInstance().fetchCardAccount(args[0]);
		if (c == null)
			return new ServiceResponse(Status.WARNING, null);
		return new ServiceResponse(Status.OK, c.getFormattedBalance());
	}

}
